# -*- coding: utf-8 -*-

# Neote -- A note taking application
#
# Copyright (C) 2009 Valéry Febvre <vfebvre@easter-eggs.com>
# http://code.google.com/p/neote/
#
# This file is part of Neote.
#
# Neote is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# Neote is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

import os.path, time
import ecore, elementary

from database import db, get_note_title

DATA_DIR = '/usr/share/neote/'
DATA_DIR = '../data/'

STATUS_MSG_TIMEOUT = 3


class NotePageCommon(object):
    def __init__(self, main):
        self.main = main
        self.category_edit_dlg = CategoryEditDialog()

        # main box
        self.box = elementary.Box(self.main.win)
        self.box.size_hint_weight_set(1, 1)
        self.box.size_hint_align_set(-1, -1)
        self.box.show()

        # pager
        self.pager = elementary.Pager(self.main.win)
        self.pager.size_hint_weight_set(1.0, 1.0)
        self.pager.size_hint_align_set(-1.0, -1.0)
        self.box.pack_end(self.pager)
        self.pager.show()

        # elements in 1st page: title & list
        self.box_page_1 = elementary.Box(self.main.win)
        self.box_page_1.size_hint_weight_set(1, 1)
        self.box_page_1.size_hint_align_set(-1, -1)
        self.box_page_1.show()

        self.title_1 = elementary.Label(self.main.win)
        self.title_1.label_set('')
        self.box_page_1.pack_end(self.title_1)
        self.title_1.show()

        self.list_page_1 = elementary.List(self.main.win)
        self.list_page_1.scale_set(.75)
        self.list_page_1.size_hint_weight_set(1.0, 1.0)
        self.list_page_1.size_hint_align_set(-1.0, -1.0)
        self.box_page_1.pack_end(self.list_page_1)
        self.list_page_1.show()
        self.list_page_1_selected_item = None

        self.label_status = elementary.Label(self.main.win)
        self.label_status.label_set('')
        self.label_status.size_hint_weight_set(0.0, 0.0)
        self.label_status.size_hint_align_set(0.5, 0.5)
        self.box_page_1.pack_end(self.label_status)
        self.label_status.show()
        self.label_status_timer = None

        self.pager.content_push(self.box_page_1)

        # elements in 2nd page: text entry
        self.box_page_2 = elementary.Box(self.main.win)
        self.box_page_2.size_hint_weight_set(1, 1)
        self.box_page_2.size_hint_align_set(-1, -1)
        self.box_page_2.show()

        self.title_2 = elementary.Label(self.main.win)
        self.title_2.label_set('')
        self.title_2.size_hint_weight_set(1, 0)
        self.title_2.size_hint_align_set(1, 0)
        self.box_page_2.pack_end(self.title_2)
        self.title_2.show()

        self.scroller_text = elementary.Scroller(self.main.win)
        self.scroller_text.bounce_set(False, False)
        self.scroller_text.size_hint_weight_set(1.0, 1.0)
        self.scroller_text.size_hint_align_set(-1.0, -1.0)
        self.box_page_2.pack_end(self.scroller_text)
        self.scroller_text.show()

        self.entry_text = None

        self.pager.content_push(self.box_page_2)

        # elements in 3rd page: add & categories list
        self.box_page_3 = elementary.Box(self.main.win)
        self.box_page_3.size_hint_weight_set(1, 1)
        self.box_page_3.size_hint_align_set(-1, -1)
        self.box_page_3.show()

        self.box_category_add = elementary.Box(self.main.win)
        self.box_category_add.horizontal_set(True)
        self.box_category_add.size_hint_weight_set(1, 0)
        self.box_category_add.size_hint_align_set(-1, 0)
        self.box_category_add.show()

        self.btn_category_add = elementary.Button(self.main.win)
        self.btn_category_add.label_set('Add')
        self.btn_category_add.clicked = self._add_category
        self.box_category_add.pack_end(self.btn_category_add)
        self.btn_category_add.show()

        scroller = elementary.Scroller(self.main.win)
        scroller.bounce_set(False, False)
        scroller.size_hint_weight_set(1.0, 1.0)
        scroller.size_hint_align_set(-1.0, -1.0)
        scroller.show()

        self.entry_category_add = elementary.Entry(self.main.win)
        self.entry_category_add.scale_set(2)
        self.entry_category_add.single_line_set(True)
        self.entry_category_add.size_hint_weight_set(1, 1)
        self.entry_category_add.size_hint_align_set(-1, -1)
        scroller.content_set(self.entry_category_add)
        self.entry_category_add.show()

        self.box_category_add.pack_end(scroller)
        self.box_page_3.pack_end(self.box_category_add)

        self.list_page_3 = elementary.List(self.main.win)
        self.list_page_3.scale_set(.75)
        self.list_page_3.size_hint_weight_set(1.0, 1.0)
        self.list_page_3.size_hint_align_set(-1.0, -1.0)
        self.box_page_3.pack_end(self.list_page_3)
        self.list_page_3.show()
        self.list_page_3_selected_item = None
        self.list_page_3_checks = None

        self.pager.content_push(self.box_page_3)

        # toolbar
        box_toolbar = elementary.Box(self.main.win)
        box_toolbar.scale_set(.75)
        box_toolbar.size_hint_weight_set(1.0, 0)
        box_toolbar.size_hint_align_set(-1.0, 0)
        box_toolbar.show()

        self.toolbar = elementary.Toolbar(self.main.win)
        self.toolbar.size_hint_align_set(-1.0, 0)
        self.toolbar_size_min = None

        self.toolbar_item_text = self.toolbar.item_add(
            NeoteIcon(self.main.win, 'note-text.png'), "Text", self.show_text)
        self.toolbar_item_categories = self.toolbar.item_add(
            NeoteIcon(self.main.win, 'note-tag.png'), "Categories", self.show_categories)

        box_toolbar.pack_end(self.toolbar)
        self.box.pack_end(box_toolbar)

        # actions buttons boxes
        self.box_actions_1 = elementary.Box(self.main.win)
        self.box_actions_1.horizontal_set(True)
        self.box_actions_1.homogenous_set(True)
        self.box_actions_1.size_hint_weight_set(1.0, 0)
        self.box_actions_1.size_hint_align_set(-1.0, 0)
        self.box.pack_end(self.box_actions_1)
        self.box_actions_1_size_min = None

        self.box_actions_2 = elementary.Box(self.main.win)
        self.box_actions_2.horizontal_set(True)
        self.box_actions_2.homogenous_set(True)
        self.box_actions_2.size_hint_weight_set(1.0, 0)
        self.box_actions_2.size_hint_align_set(-1.0, 0)
        self.box.pack_end(self.box_actions_2)
        self.box_actions_2_size_min = None

        self.main.pager.content_push(self.box)

        self.pager.content_promote(self.box_page_1)

    def _add_category(self, *args):
        name = self.entry_category_add.entry_get()
        name = name.replace('<br>', '')
        name = name.strip()
        if not name:
            return
        if db.get_category(name = name):
            # category already exists
            return

        self.entry_category_add.entry_set('')

        db.add_category(name)
        # refresh categories lists
        self._populate_categories_list()
        if self.__class__.__name__ == 'AddNotePage':
            self.main.notes_page._populate_categories_list()
        elif self.__class__.__name__ == 'NotesPage':
            self.main.add_note_page._populate_categories_list()

    def _create_entry_text(self, text, editable):
        # editable property of a entry text can be modified !!!
        # entry must be deleted/recreated each time the editable property is changed
        if self.entry_text:
            self.entry_text.delete()
        self.entry_text = elementary.Entry(self.main.win)
        self.entry_text.editable_set(editable)
        if text:
            self.entry_text.entry_set(text.encode('utf-8'))
        self.entry_text.scale_set(1.25)
        self.entry_text.size_hint_weight_set(1, 1)
        self.entry_text.size_hint_align_set(-1, -1)
        self.scroller_text.content_set(self.entry_text)
        self.entry_text.show()

    def _on_btn_edit_clicked(self, btn, action, category_id):
        self.category_edit_dlg.open(category_id, self._edit_category)

    def _edit_category(self, action, id, name = None):
        if action == 'update':
            name = name.replace('<br>', '')
            name = name.strip()
            if not name:
                return
            if db.get_category(name = name):
                # category already exists
                return
            db.update_category(id, name)
        elif action == 'delete':
            db.delete_category(id)

        # refresh notes browser & categories lists
        self.main.notes_page.browse(refresh_only = True)
        self._populate_categories_list()
        if self.__class__.__name__ == 'AddNotePage':
            self.main.notes_page._populate_categories_list()
        elif self.__class__.__name__ == 'NotesPage':
            self.main.add_note_page._populate_categories_list()

    def _on_list_page_1_item_selected(self, item, action, data):
        self.list_page_1_selected_item = data

    def _on_list_page_3_item_selected(self, item, action, data):
        self.list_page_3_selected_item = data

    def _populate_categories_list(self):
        checked_categories = []
        if self.list_page_3_checks:
            for category_id, check in self.list_page_3_checks.items():
                if check.state_get():
                    checked_categories.append(category_id)

        self.list_page_3.clear()
        self.list_page_3_checks = {}
        for category in db.get_categories():
            name = category['name'].encode('utf-8')
            # edit btn
            btn_edit = elementary.Button(self.main.win)
            btn_edit.scale_set(1)
            btn_edit.label_set("Edit")
            btn_edit.size_hint_weight_set(1.0, 1.0)
            btn_edit.size_hint_align_set(-1.0, -1.0)
            btn_edit.clicked = (self._on_btn_edit_clicked, category['id'])
            btn_edit.show()
            # check
            check = elementary.Check(self.main.win)
            check.scale_set(2)
            if category['id'] in checked_categories:
                check.state_set(True)
            check.show()
            self.list_page_3_checks[category['id']] = check
            self.list_page_3.item_append(name, btn_edit, check, self._on_list_page_3_item_selected, category['id'])
        self.list_page_3.go()

    def set_status(self, msg, timeout = STATUS_MSG_TIMEOUT):
        self.label_status.label_set('<b>%s</b>' % msg)
        if timeout:
            if self.label_status_timer:
                self.label_status_timer.delete()
            self.label_status_timer = ecore.timer_add(timeout, self.clear_status)

    def clear_status(self):
        self.label_status.label_set('')

    def show_text(self, *args):
        self.pager.content_promote(self.box_page_2)
        self.toolbar_item_text.select()

    def show_categories(self, *args):
        self.pager.content_promote(self.box_page_3)
        self.toolbar_item_categories.select()

    def show_box_actions_1(self):
        if self.box_actions_1_size_min:
            self.box_actions_1.size_hint_min = self.box_actions_1_size_min
        self.box_actions_1.show()

    def show_box_actions_2(self):
        if self.box_actions_2_size_min:
            self.box_actions_2.size_hint_min = self.box_actions_2_size_min
        self.box_actions_2.show()

    def show_toolbar(self):
        self.toolbar.size_hint_min = self.toolbar_size_min
        self.toolbar.show()

    def hide_box_actions_1(self):
        if not self.box_actions_1.visible and self.box_actions_1_size_min:
            return

        self.box_actions_1_size_min = self.box_actions_1.size_hint_min_get()
        self.box_actions_1.size_hint_min_set(0, 0)
        self.box_actions_1.hide()

    def hide_box_actions_2(self):
        if not self.box_actions_2.visible and self.box_actions_2_size_min:
            return

        self.box_actions_2_size_min = self.box_actions_2.size_hint_min_get()
        self.box_actions_2.size_hint_min_set(0, 0)
        self.box_actions_2.hide()

    def hide_toolbar(self):
        if not self.toolbar.visible and self.toolbar_size_min:
            return

        self.toolbar_size_min = self.toolbar.size_hint_min_get()
        self.toolbar.size_hint_min_set(0, 0)
        self.toolbar.hide()


#
# "Add note" page
#

class AddNotePage(NotePageCommon):
    def __init__(self, main):
        NotePageCommon.__init__(self, main)

        # action buttons
        self.btn_cancel = elementary.Button(self.main.win)
        self.btn_cancel.label_set("Cancel")
        self.btn_cancel.icon_set(NeoteIcon(self.main.win, 'cancel.png'))
        self.btn_cancel.size_hint_weight_set(1.0, 1.0)
        self.btn_cancel.size_hint_align_set(-1.0, -1.0)
        self.btn_cancel.clicked = (self._cancel, None)
        self.box_actions_2.pack_end(self.btn_cancel)
        self.btn_cancel.show()

        self.btn_done = elementary.Button(self.main.win)
        self.btn_done.label_set("Done")
        self.btn_done.icon_set(NeoteIcon(self.main.win, 'done.png'))
        self.btn_done.size_hint_weight_set(1.0, 1.0)
        self.btn_done.size_hint_align_set(-1.0, -1.0)
        self.btn_done.clicked = (self._save, None)
        self.box_actions_2.pack_end(self.btn_done)
        self.btn_done.show()

        # elements in 1st page: type of the note
        self.title_1.label_set('<b>Choose Type</b>')

        self.list_page_1._callback_add('clicked', (self._add_note, None))
        self.list_page_1.item_append('Text Note', None, None, self._on_list_page_1_item_selected, {'type': 'text'})
        self.list_page_1.item_append('Draw Note', None, None, self._on_list_page_1_item_selected, {'type': 'draw'})
        self.list_page_1.go()

        # elements in 2nd page: text entry
        self._create_entry_text(None, True)

        # elements in 3rd page: add & categories list
        self._populate_categories_list()

        self.hide_box_actions_1()
        self.hide_box_actions_2()
        self.hide_toolbar()

    def _add_note(self, list, action, data):
        if self.list_page_1_selected_item['type'] == 'text':
            # text note
            self.show_box_actions_2()
            self.show_toolbar()
            self.show_text()
        elif self.list_page_1_selected_item['type'] == 'draw':
            # draw note
            self.set_status('Not yet implemented')

    def _cancel(self, *args):
        self.hide_box_actions_2()
        self.hide_toolbar()
        self.pager.content_promote(self.box_page_1)

        # clear text & categories
        self.entry_text.entry_set('')
        for category_id, check in self.list_page_3_checks.items():
            check.state_set(False)

    def _save(self, *args):
        text = self.entry_text.entry_get().strip()
        if not text:
            print "note is empty !!!"
            return

        note_id = db.add_note(1, text)
        for category_id, check in self.list_page_3_checks.items():
            if check.state_get():
                db.add_note_category(note_id, category_id)

        self.main.notes_page.browse(refresh_only = True)
        self._cancel()


#
# Notes page
#

class NotesPage(NotePageCommon):
    def __init__(self, main):
        NotePageCommon.__init__(self, main)
        self.current_note = None
        self.is_edited = False

        # action buttons
        self.btn_back_1 = elementary.Button(self.main.win)
        self.btn_back_1.label_set("Back")
        self.btn_back_1.icon_set(NeoteIcon(self.main.win, 'back.png'))
        self.btn_back_1.size_hint_weight_set(1.0, 1.0)
        self.btn_back_1.size_hint_align_set(-1.0, -1.0)
        self.box_actions_1.pack_end(self.btn_back_1)
        self.btn_back_1.clicked = (self._close, None)
        self.btn_back_1.show()

        self.btn_back_2 = elementary.Button(self.main.win)
        self.btn_back_2.label_set("Back")
        self.btn_back_2.icon_set(NeoteIcon(self.main.win, 'back.png'))
        self.btn_back_2.size_hint_weight_set(1.0, 1.0)
        self.btn_back_2.size_hint_align_set(-1.0, -1.0)
        self.box_actions_2.pack_end(self.btn_back_2)
        self.btn_back_2.clicked = (self._close_note, None)
        self.btn_back_2.show()

        self.btn_delete = elementary.Button(self.main.win)
        self.btn_delete.label_set("Delete")
        self.btn_delete.icon_set(NeoteIcon(self.main.win, 'delete.png'))
        self.btn_delete.size_hint_weight_set(1.0, 1.0)
        self.btn_delete.size_hint_align_set(-1.0, -1.0)
        self.btn_delete.clicked = (self._on_btn_delete_clicked, None)
        self.box_actions_2.pack_end(self.btn_delete)
        self.btn_delete.show()

        self.btn_mode = elementary.Button(self.main.win)
        self.btn_mode.size_hint_weight_set(1.0, 1.0)
        self.btn_mode.size_hint_align_set(-1.0, -1.0)
        self.box_actions_2.pack_end(self.btn_mode)
        self.btn_mode.clicked = (self._edit_view_note, None)
        self.btn_mode.show()

        self.hide_box_actions_1()
        self.hide_toolbar()

        # elements in 1st page: list of notes
        self.list_page_1._callback_add('clicked', (self._open, None))

        # elements in 2nd page: text entry
        self._create_entry_text(None, False)

        # elements in 3rd page: add & categories list
        self._populate_categories_list()

        self.browse_state = None
        self.browse()

    def _close(self, *args):
        self.browse_state = None
        self.browse()

    def _close_note(self, *args):
        self.save_note()

        if self.is_edited:
            self.hide_toolbar()
        self.hide_box_actions_2()

        self.current_note = None
        self.is_edited = False
        self.browse()
        self.pager.content_promote(self.box_page_1)

    def _delete_note(self, *args):
        db.delete_note(self.current_note['id'])
        self.current_note = None
        self._close_note()

    def _edit_view_note(self, *args):
        if not self.is_edited:
            self.show_toolbar()

            self._create_entry_text(self.current_note['data'], True)

            self.toolbar_item_text.select()
            self._set_note_info('')
            self.is_edited = True
        else:
            self.save_note()
            self.hide_toolbar()

            self.pager.content_promote(self.box_page_2)
            self._create_entry_text(self.current_note['data'], False)

            self._set_note_info()
            self.is_edited = False
        self._toggle_btn_mode()

    def _on_btn_delete_clicked(self, *args):
        msg = 'Do you really want to delete the note <b>"%s"</>?' % get_note_title(self.current_note)
        confirm_dlg.open(msg, self._delete_note)

    def _open(self, list, action, data):
        if self.list_page_1_selected_item['type'] != 'note':
            self.browse(self.list_page_1_selected_item)
        else:
            self.open_note(self.list_page_1_selected_item['id'])

    def open_note(self, id):
        # close previous opened note
        # it's necessary when a new note is opened from the "Search" page
        if self.current_note:
            self._close_note()

        # open note
        self.show_text()
        self.hide_box_actions_1()
        self.show_box_actions_2()

        self._toggle_btn_mode()
        self.current_note = db.get_note(id)

        # info
        self._set_note_info()
        # text
        self._create_entry_text(self.current_note['data'], False)

        note_categories = []
        for couple in db.get_note_categories(self.current_note['id']):
            note_categories.append(couple['category_id'])

        for category_id, check in self.list_page_3_checks.items():
            if category_id in note_categories:
                check.state_set(True)
            else:
                check.state_set(False)

    def save_note(self, *args):
        if not self.current_note or not self.is_edited:
            return

        # save text
        db.update_note(self.current_note['id'], self.entry_text.entry_get())
        self.current_note = db.get_note(self.current_note['id'])

        # save categories
        old_categories = []
        for couple in db.get_note_categories(self.current_note['id']):
            old_categories.append(couple['category_id'])
        new_categories = []
        for category_id, check in self.list_page_3_checks.items():
            if check.state_get():
                new_categories.append(category_id)
        for category_id in set(old_categories) ^ set(new_categories):
            if category_id in old_categories:
                db.delete_note_category(self.current_note['id'], category_id)
            else:
                db.add_note_category(self.current_note['id'], category_id)

    def _set_note_info(self, info = None):
        if info is None:
            # last update
            info = "<em>%s</>" % time.strftime('%D %T', time.localtime(self.current_note['updated']))
        self.title_2.label_set(info)

    def _toggle_btn_mode(self):
        if self.is_edited:
            self.btn_mode.label_set("View Mode")
            self.btn_mode.icon_set(NeoteIcon(self.main.win, 'note-edit-close.png'))
        else:
            self.btn_mode.label_set("Edit Mode")
            self.btn_mode.icon_set(NeoteIcon(self.main.win, 'note-edit.png'))

    def browse(self, state = None, refresh_only = False):
        if refresh_only and self.current_note:
            # no need to refresh if a note is currently opened
            # browser will be refresh when user will click on "Back" or "Delete" button
            return

        if state:
            self.browse_state = state

        self.list_page_1.clear()

        if not self.browse_state:
            self.hide_box_actions_1()
            self.hide_box_actions_2()
            self.title_1.label_set('')

            # added item "All" to display all notes
            notes = db.get_notes()
            if len(notes):
                icon = NeoteIcon(self.main.win, 'folder.png')
                icon.scale_set(1, 0)
                self.list_page_1.item_append(
                    'All [%d]' % len(notes), icon, None,
                    self._on_list_page_1_item_selected, {'type': 'category', 'id': None})

            # added an item foreach category
            for category in db.get_categories():
                notes = db.get_notes(category_id = category['id'])
                if len(notes) == 0:
                    continue
                icon = NeoteIcon(self.main.win, 'folder-tag.png')
                icon.scale_set(1, 0)
                self.list_page_1.item_append(
                    '%s [%d]' % (category['name'].encode('utf-8'), len(notes)), icon, None,
                    self._on_list_page_1_item_selected, {'type': 'category', 'id': category['id']})

            # added item "Uncategorized" for notes without category
            notes = db.get_notes(category_id = 'uncategorized')
            if len(notes):
                icon = NeoteIcon(self.main.win, 'folder.png')
                icon.scale_set(1, 0)
                self.list_page_1.item_append(
                    'Uncategorized [%d]' % len(notes), icon, None,
                    self._on_list_page_1_item_selected, {'type': 'category', 'id': 'uncategorized'})
        else:
            self.show_box_actions_1()
            if self.browse_state['id'] is None:
                self.title_1.label_set('<b>All Notes</>')
            elif self.browse_state['id'] == 'uncategorized':
                self.title_1.label_set('<b>Uncategorized Notes</>')
            else:
                category = db.get_category(self.browse_state['id'])
                if not category:
                    # current selected/opened category was deleted
                    self.browse_state = None
                    self.browse()
                    return
                self.title_1.label_set('<b>"%s" Notes</>' % category['name'].encode('utf-8'))

            for note in db.get_notes(category_id = self.browse_state['id']):
                icon = NeoteIcon(self.main.win, 'note-text-42.png')
                icon.scale_set(1, 0)
                self.list_page_1.item_append(
                    get_note_title(note), icon, None,
                    self._on_list_page_1_item_selected, {'type': 'note', 'id': note['id']})

        self.list_page_1.go()


#
# Search page
#

class SearchPage(object):
    def __init__(self, main):
        self.main = main

        # main box
        self.box = elementary.Box(self.main.win)
        self.box.size_hint_weight_set(1, 1)
        self.box.size_hint_align_set(-1, -1)
        self.box.show()

        self.label = elementary.Label(self.main.win)
        self.label.label_set('')
        self.label.size_hint_weight_set(0, 0)
        self.label.size_hint_align_set(0.5, 0.5)
        self.box.pack_end(self.label)
        self.label.show()

        # search box: search button + entry
        box_search = elementary.Box(self.main.win)
        box_search.horizontal_set(True)
        box_search.size_hint_weight_set(1, 0)
        box_search.size_hint_align_set(-1, 0)
        box_search.show()

        btn_search = elementary.Button(self.main.win)
        btn_search.label_set('Search')
        btn_search.clicked = self._search
        box_search.pack_end(btn_search)
        btn_search.show()

        scroller = elementary.Scroller(self.main.win)
        scroller.bounce_set(False, False)
        scroller.size_hint_weight_set(1.0, 1.0)
        scroller.size_hint_align_set(-1.0, -1.0)
        scroller.show()

        self.entry_search = elementary.Entry(self.main.win)
        self.entry_search.scale_set(2)
        self.entry_search.single_line_set(True)
        self.entry_search.size_hint_weight_set(1, 1)
        self.entry_search.size_hint_align_set(-1, -1)
        scroller.content_set(self.entry_search)
        self.entry_search.show()

        box_search.pack_end(scroller)
        self.box.pack_end(box_search)

        # results list
        self.list = elementary.List(self.main.win)
        self.list.scale_set(.75)
        self.list.size_hint_weight_set(1.0, 1.0)
        self.list.size_hint_align_set(-1.0, -1.0)
        self.box.pack_end(self.list)
        self.list.show()
        self.list_selected_item = None
        self.list._callback_add('clicked', (self._open_note, None))

        self.main.pager.content_push(self.box)

    def _on_list_item_selected(self, item, action, data):
        self.list_selected_item = data

    def _open_note(self, *args):
        self.main.notes_page.open_note(self.list_selected_item['id'])
        self.main.show_notes_page()

    def _search(self, *args):
        self.list.clear()
        self.label.label_set('')

        words = self.entry_search.entry_get().replace('<br>', '').strip()
        if not words:
            return

        notes = db.search_notes(words)

        if notes:
            self.label.label_set('<b>%d note(s) found</>' % len(notes))
            for note in notes:
                icon = NeoteIcon(self.main.win, 'note-text-42.png')
                icon.scale_set(1, 0)
                self.list.item_append(
                    get_note_title(note), icon, None, self._on_list_item_selected, {'id': note['id']})

            self.list.go()


#
# Preferences page
#

class PreferencesPage(object):
    def __init__(self, main):
        self.main = main
        self.fullscreen = False

        self.box = elementary.Box(self.main.win)
        self.box.size_hint_weight_set(1, 1)
        self.box.size_hint_align_set(-1, -1)
        self.box.show()

        sc = elementary.Scroller(self.main.win)
        sc.bounce_set(False, False)
        sc.size_hint_weight_set(1.0, 1.0)
        sc.size_hint_align_set(-1.0, -1.0)
        self.box.pack_end(sc)
        sc.show()

        box = elementary.Box(self.main.win)
        box.size_hint_weight_set(1.0, 0)
        box.size_hint_align_set(-1, 0)
        sc.content_set(box)
        box.show()

        if hasattr(self.main.win, 'fullscreen_set'):
            self.tg_fullscreen = elementary.Toggle(self.main.win)
            self.tg_fullscreen.icon_set(NeoteIcon(self.main.win, 'fullscreen.png'))
            self.tg_fullscreen.label_set("Fullscreen")
            self.tg_fullscreen.size_hint_weight_set(1, 0)
            self.tg_fullscreen.size_hint_align_set(-1, 0)
            self.tg_fullscreen.state_set(self.fullscreen)
            self.tg_fullscreen.states_labels_set("Yes", "No")
            self.tg_fullscreen.changed = self.toggle_fullscreen
            box.pack_end(self.tg_fullscreen)
            self.tg_fullscreen.show()

        self.main.pager.content_push(self.box)

    def toggle_fullscreen(self, *args):
        state = self.tg_fullscreen.state_get()
        if state != self.fullscreen:
            self.main.win.fullscreen_set(state)
            self.fullscreen = state


#
# Dialogs
#

class CategoryEditDialog(object):
    def __init__(self):
        self.win = None

    def _create(self):
        title = 'Edit a category'
        self.win = elementary.Window(title, elementary.ELM_WIN_BASIC)
        self.win.title_set(title)
        self.win.destroy = (self._close, None)

        bg = elementary.Background(self.win)
        self.win.resize_object_add(bg)
        bg.size_hint_weight_set(1.0, 1.0)
        bg.show()

        box = elementary.Box(self.win)
        box.size_hint_weight_set(1, 1)
        box.size_hint_align_set(-1, -1)
        box.show()

        # entry to edit category name
        frame = elementary.Frame(self.win)
        frame.size_hint_weight_set(1, 1)
        frame.size_hint_align_set(-1, -1)
        frame.label_set("Category's name")
        box.pack_end(frame)
        frame.show()

        scroller = elementary.Scroller(self.win)
        scroller.bounce_set(False, False)
        scroller.size_hint_weight_set(1.0, 1.0)
        scroller.size_hint_align_set(-1.0, -1.0)
        frame.content_set(scroller)
        scroller.show()

        self.entry_category_name = elementary.Entry(self.win)
        self.entry_category_name.scale_set(2)
        self.entry_category_name.single_line_set(True)
        self.entry_category_name.size_hint_weight_set(1, 1)
        self.entry_category_name.size_hint_align_set(-1, -1)
        scroller.content_set(self.entry_category_name)
        self.entry_category_name.show()

        # entry to display information about category
        frame = elementary.Frame(self.win)
        frame.size_hint_weight_set(1, 1)
        frame.size_hint_align_set(-1, -1)
        frame.label_set("Information about this category")
        box.pack_end(frame)
        frame.show()

        scroller = elementary.Scroller(self.win)
        scroller.bounce_set(False, False)
        scroller.size_hint_weight_set(1.0, 1.0)
        scroller.size_hint_align_set(-1.0, -1.0)
        frame.content_set(scroller)
        scroller.show()

        self.entry_category_info = elementary.Entry(self.win)
        self.entry_category_info.editable_set(False)
        self.entry_category_info.size_hint_weight_set(1, 1)
        self.entry_category_info.size_hint_align_set(-1, -1)
        scroller.content_set(self.entry_category_info)
        self.entry_category_info.show()

        # buttons: cancel, delete, save
        box_actions = elementary.Box(self.win)
        box_actions.horizontal_set(True)
        box_actions.homogenous_set(True)
        box_actions.size_hint_weight_set(1, 0)
        box_actions.size_hint_align_set(-1, 0)
        box_actions.show()
        box.pack_end(box_actions)

        btn_cancel = elementary.Button(self.win)
        btn_cancel.label_set("Cancel")
        btn_cancel.icon_set(NeoteIcon(self.win, 'cancel.png'))
        btn_cancel.size_hint_weight_set(1.0, 1.0)
        btn_cancel.size_hint_align_set(-1.0, -1.0)
        btn_cancel.clicked = (self._close, None)
        box_actions.pack_end(btn_cancel)
        btn_cancel.show()

        btn_delete = elementary.Button(self.win)
        btn_delete.label_set("Delete")
        btn_delete.icon_set(NeoteIcon(self.win, 'delete.png'))
        btn_delete.size_hint_weight_set(1.0, 1.0)
        btn_delete.size_hint_align_set(-1.0, -1.0)
        btn_delete.clicked = (self._on_btn_delete_clicked, None)
        box_actions.pack_end(btn_delete)
        btn_delete.show()

        btn_save = elementary.Button(self.win)
        btn_save.label_set("Done")
        btn_save.icon_set(NeoteIcon(self.win, 'done.png'))
        btn_save.size_hint_weight_set(1.0, 1.0)
        btn_save.size_hint_align_set(-1.0, -1.0)
        btn_save.clicked = (self._save, None)
        box_actions.pack_end(btn_save)
        btn_save.show()

        self.win.resize_object_add(box)

    def _close(self, *args):
        self.win.hide()

    def _on_btn_delete_clicked(self, *args):
        msg = 'Do you really want to delete the category "%s"?' % self.category['name'].encode('utf-8')
        confirm_dlg.open(msg, self._delete)

    def _delete(self, *args):
        self.on_changed_cb('delete', self.category['id'])
        self._close()

    def _save(self, *args):
        self.on_changed_cb('update', self.category['id'], self.entry_category_name.entry_get())
        self._close()

    def open(self, id, on_changed_cb, **kwargs):
        if not self.win:
            self._create()

        self.on_changed_cb = on_changed_cb

        self.category = db.get_category(id)
        self.entry_category_name.entry_set(self.category['name'].encode('utf-8'))

        notes = db.get_notes(category_id = id)
        if len(notes) == 0:
            info = 'No note uses this category.'
        else:
            if len(notes) == 1:
                info = 'Only 1 note uses this category:'
            else:
                info = '%d notes use this category:' % len(notes)
            for note in notes:
                info += '<br>   <b>#</> %s' % get_note_title(note)
        self.entry_category_info.entry_set(info)

        self.win.resize(480, 480)
        self.win.show()


class ConfirmDialog(object):
    def __init__(self, title = 'Confirm ?'):
        self.win = None
        self.title = title

    def _create(self):
        self.win = elementary.Window(self.title, elementary.ELM_WIN_BASIC)
        self.win.title_set(self.title)
        self.win.destroy = (self._close, None)

        bg = elementary.Background(self.win)
        self.win.resize_object_add(bg)
        bg.size_hint_weight_set(1.0, 1.0)
        bg.show()

        box = elementary.Box(self.win)
        box.size_hint_weight_set(1, 1)
        box.size_hint_align_set(-1, -1)
        box.show()

        table = elementary.Table(self.win)
        table.homogenous_set(True)
        table.size_hint_weight_set(1, 1)
        table.size_hint_align_set(-1, -1)

        label = elementary.Label(self.win)
        table.pack(label, 0, 0, 1, 1)

        self.entry_title = elementary.Entry(self.win)
        self.entry_title.line_wrap_set(True)
        self.entry_title.editable_set(False)
        self.entry_title.entry_set('')
        self.entry_title.size_hint_weight_set(1, 1)
        table.pack(self.entry_title, 1, 1, 1, 1)
        self.entry_title.show()

        label = elementary.Label(self.win)
        table.pack(label, 2, 2, 1, 1)

        box.pack_end(table)
        table.show()

        box_actions = elementary.Box(self.win)
        box_actions.horizontal_set(True)
        box_actions.homogenous_set(True)
        box_actions.size_hint_weight_set(1, 0)
        box_actions.size_hint_align_set(-1, -1)
        box_actions.show()
        box.pack_end(box_actions)

        btn_yes = elementary.Button(self.win)
        btn_yes.label_set("Yes")
        btn_yes.icon_set(NeoteIcon(self.win, 'yes.png'))
        btn_yes.size_hint_weight_set(1.0, 1.0)
        btn_yes.size_hint_align_set(-1.0, -1.0)
        btn_yes.clicked = (self._on_btn_yes_clicked, None)
        box_actions.pack_end(btn_yes)
        btn_yes.show()

        btn_no = elementary.Button(self.win)
        btn_no.label_set("No")
        btn_no.icon_set(NeoteIcon(self.win, 'close.png'))
        btn_no.size_hint_weight_set(1.0, 1.0)
        btn_no.size_hint_align_set(-1.0, -1.0)
        btn_no.clicked = (self._close, None)
        box_actions.pack_end(btn_no)
        btn_no.show()

        self.win.resize_object_add(box)

    def open(self, message, confirm_cb, **kwargs):
        if not self.win:
            self._create()

        self.entry_title.entry_set(message)
        self.confirm_cb = confirm_cb
        self.confirm_cb_args = kwargs
        self.win.resize(480, 480)
        self.win.show()

    def _on_btn_yes_clicked(self, *args):
        self._close()
        self.confirm_cb(**self.confirm_cb_args)

    def _close(self, *args):
        self.win.hide()

confirm_dlg = ConfirmDialog()


#
# Utils
#

class NeoteIcon(elementary.Icon):
    def __init__(self, parent, image):
        elementary.Icon.__init__(self, parent)
        self.file_set(os.path.join(DATA_DIR, 'images', image))
